package main

import (
	"fmt"
	"gitee.com/zy_blue7/go-learn/func-learn"
	"gitee.com/zy_blue7/go-learn/reflect-learn"
	"math"
	"reflect"
)

type myInt int64

func main() {

	max := func_learn.Max(11, 19)

	fmt.Println(max)

	a1, b1 := func_learn.Swap("100", "99")

	fmt.Println(a1)
	fmt.Println(b1)

	//--------------------引用传值---------------------------------------
	/* 定义局部变量 */
	var a int = 100
	var b int = 200

	fmt.Printf("交换前，a 的值 : %d\n", a)
	fmt.Printf("交换前，b 的值 : %d\n", b)

	/* 调用 SwapP() 函数
	 * &a 指向 a 指针，a 变量的地址
	 * &b 指向 b 指针，b 变量的地址
	 */
	func_learn.SwapP(&a, &b)

	fmt.Printf("交换后，a 的值 : %d\n", a)
	fmt.Printf("交换后，b 的值 : %d\n", b)

	//	-----------------------函数作为参数 传递到函数中---------------------------------------------

	/* 声明函数变量 */
	getSquareRoot := func(x float64) float64 {
		return math.Sqrt(x)
	}

	/* 使用函数 */
	fmt.Println(getSquareRoot(9))

	//----------------------------闭包----------------------------------------------------------

	/* nextNumber 为一个函数，函数 i 为 0 */
	nextNumber := func_learn.GetSequence()

	/* 调用 nextNumber 函数，i 变量自增 1 并返回 */
	fmt.Println(nextNumber())
	fmt.Println(nextNumber())
	fmt.Println(nextNumber())

	//todo 由于nextNumber 是一个函数，并没有释放，所以该函数内的i并没有释放，所以调用一次，i就会增加一次
	//todo  当重新调用 func_learn.GetSequence() 时，重新创建一个i， 继续。。。。

	/* 创建新的函数 nextNumber1，并查看结果 */
	nextNumber1 := func_learn.GetSequence()
	fmt.Println(nextNumber1())
	fmt.Println(nextNumber1())

	//	-----------------------------todo 反射------------------------------------------------------------
	stu := reflect_learn.Student{
		User: reflect_learn.User{
			UserName: "ourlang",
			Password: "123456",
			Address:  "成都",
		},
		Name: "福小林",
		Age:  "18",
	}

	typeOf := reflect.TypeOf(stu)
	//返回stu的第二个字段Name
	//{Name  string  48 [1] false}
	fmt.Println(typeOf.FieldByIndex([]int{1}))
	//返回stu的第二个字段Name 因为Name不是结构体 你再去取Name的第一个字段就会报错
	//panic: reflect: Field of non-struct type
	//fmt.Println(typeOf.FieldByIndex([]int{1,0}))

	//返回stu的第一个字段User结构体中的第一个字段UserName
	//{UserName  string  0 [0] false}
	fmt.Println(typeOf.FieldByIndex([]int{0, 0}))
	//返回stu的第一个字段User结构体中的第三个字段Address
	//{Address  string  32 [2] false}
	fmt.Println(typeOf.FieldByIndex([]int{0, 2}))

	fmt.Println("--------------------------------")

	var ae *float32                  // 指针
	var be myInt                     // 自定义类型
	var c rune                       // 类型别名
	reflect_learn.GetReflectType(ae) // type: kind:ptr
	reflect_learn.GetReflectType(be) // type:myInt kind:int64
	reflect_learn.GetReflectType(c)  // type:int32 kind:int32
	p1 := reflect_learn.Person{
		UserName: "福小林",
		Password: "123456",
		Age:      28,
		Address:  "成都市",
		Flag:     true,
	}
	reflect_learn.GetReflectType(p1) //type:User kind:struct

	perosnType := reflect.TypeOf(p1)
	//该类型的方法个数
	numMethod := perosnType.NumMethod()
	for i := 0; i < numMethod; i++ {
		//也可以通过MethodByName查找类型对应的方法
		//method, b := stuType.MethodByName("Say")
		myMethod := perosnType.Method(i)
		//方法名   方法类型  方法以接收者为第一个参数
		fmt.Println(myMethod.Name, myMethod.Type, myMethod.Func)
	}

	reflect_learn.GetReflectType(perosnType) //type:User kind:struct
	//如果u1是结构体指针，则需要进行解耦elem(),否则会出现恐慌
	//numField := stuType.Elem().NumField()
	numField := perosnType.NumField()
	for i := 0; i < numField; i++ {
		//也可以通过FieldByName来找对应结构体字段
		//structField, b := stuType.FieldByName("UserName")

		//如果u1是结构体指针，则需要进行解耦elem(),否则会出现恐慌
		//field := stuType.Elem().Field(i)
		field := perosnType.Field(i)
		// 字段名称和字段类型
		fmt.Println(field.Name, field.Type)
		//是否是匿名字段和在结构体的索引位置
		fmt.Println(field.Anonymous, field.Index)
		// 结构中的偏移量（字节)和 字段标签字符串
		fmt.Println(field.Offset, field.Tag)
		//字段在结构体中的路径,限定小写字母开头字段名(未报告)包路径。大写字母开头的包路径为空。
		//这里只有flag字段有包路径
		fmt.Println(field.PkgPath)
	}

	fmt.Println("------------------------------------------------------------------------------")

	var e float32 = 3.14
	var f int64 = 100
	reflect_learn.GetReflectValue(e) // type is float32, value is 3.140000
	reflect_learn.GetReflectValue(f) // type is int64, value is 100
	// 将int类型的原始值转换为reflect.Value类型
	g := reflect.ValueOf(10)
	fmt.Printf("type g :%T\n", g) // type g :reflect.Value

	str := "hello world"
	//1 修改普通类型
	reflect.ValueOf(&str).Elem().SetString("张三")
	fmt.Println(str) //张三

	//2 修改结构体
	//2.1 ValueOf()传入一个变量的地址,返回是变量的地址
	userValue := reflect.ValueOf(&p1)
	//reflect.Value  &main.User{UserName:"福小林", Password:"123456", Age:28, Address:"成都市", flag:true}
	fmt.Printf("%T  %#v\n", userValue, userValue)
	//2.2 Elem()：返回的是变量的原始值
	elem := userValue.Elem()
	//reflect.Value  main.User{UserName:"福小林", Password:"123456", Age:28, Address:"成都市", flag:true}
	fmt.Printf("%T  %#v\n", elem, elem)
	//2.3 通过FieldByName()：传入结构体字段名称   SetString():传入你要修改的变量值
	elem.FieldByName("UserName").SetString("李四")
	elem.FieldByName("Age").SetInt(19)
	elem.FieldByName("Password").Set(reflect.ValueOf("admin"))
	fmt.Println(p1) //{李四 admin 19 成都市 true}

	fmt.Println("---------------------------------------")

	//无参数方法调用
	reflect.ValueOf(&p1).MethodByName("Say").Call([]reflect.Value{})       //福小林说话了
	reflect.ValueOf(p1).MethodByName("Say").Call(make([]reflect.Value, 0)) //福小林说话了

	//有参数方法调用，请注意顺序
	reflect.ValueOf(p1).MethodByName("Eat").Call([]reflect.Value{reflect.ValueOf("苹果")})                               //福小林吃的是苹果
	reflect.ValueOf(p1).MethodByName("Entertain").Call([]reflect.Value{reflect.ValueOf("安凌阳"), reflect.ValueOf("牛排")}) //福小林请安凌阳吃了牛排

}
